package controllers

import (
	"fmt"
	"net/http"
	"time"

	"example.com/gojwt/database"
	"example.com/gojwt/models"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt"
	"golang.org/x/crypto/bcrypt"
)

const SecretKey = "secret"

func Register(c *gin.Context) {
	var data map[string]string

	if err := c.ShouldBindJSON(&data); err != nil {
		panic(err)
	}

	password, err := bcrypt.GenerateFromPassword([]byte(data["password"]), bcrypt.DefaultCost)

	if err != nil {
		c.JSON(http.StatusBadRequest, nil)
	}

	user := models.User{
		// Nickname: data["nickname"],
		// Username: data["username"],
		Email:    data["email"],
		Password: string(password),
	}
	database.DB.Create(&user)

	c.JSON(http.StatusCreated, gin.H{"result": user.Email})
}

func Login(c *gin.Context) {

	var data map[string]string
	if err := c.ShouldBindJSON(&data); err != nil {
		panic(err)
	}

	var user models.User
	database.DB.Where("email = ?", data["email"]).First(&user)

	if user.Id == 0 {
		c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"detail": "用户不存在"})
		return
	}

	if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(data["password"])); err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"detail": "密码不正确"})
		return
	}

	// 更新最后一次登录时间
	// database.DB.Model(&user).Update("last_login", time.Now())

	// 获取token
	token, err := generateToken(user.Email)

	if err != nil {
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"detail": "服务器错误"})
		return
	}

	// 设置cookie TODO:前端使用localstorage
	c.SetCookie("jwt", token, 24, "/", "localhost", false, true)

	c.JSON(http.StatusOK, gin.H{"access_token": token})
}

func generateToken(email string) (string, error) {
	claims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
		ExpiresAt: time.Now().Add(time.Hour * 24).Unix(),
		IssuedAt:  time.Now().Unix(),
		Issuer:    email,
	})

	token, err := claims.SignedString([]byte(SecretKey))
	if err != nil {
		return "", err
	}

	return token, nil
}

// httponly cookie cors : AllowCredentials: true
func User(c *gin.Context) {
	tokenStr, err := c.Cookie("jwt")
	if err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"detail": "bad request"})
		return
	}

	token, err := ValidateToken(tokenStr)

	if err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"detail": "bad request"})
		return
	}

	if token.Valid {
		claims := token.Claims.(jwt.MapClaims)
		c.JSON(http.StatusOK, gin.H{"detail": claims["iss"]})
	} else {
		c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"detail": "unauthorized"})
		return
	}
}

func Logout(c *gin.Context) {
	// 设置为过期
	c.SetCookie("jwt", "", -1, "/", "localhost", false, true)
	c.JSON(http.StatusNoContent, nil)
}

func ValidateToken(tokenString string) (*jwt.Token, error) {
	return jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		// siging method validation
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}

		// return the secret signing key
		return []byte(SecretKey), nil
	})
}
